package org.example;

import java.util.function.LongSupplier;

/**
 * @author lijinting01
 */
public abstract class SnowflakeIdWorker {

  private final int timestampShift;

  private final int dataCenterIdShift;

  private final int workerIdShift;

  private final long sequenceMax;

  private final long epoch;

  private final long dataCenterId;

  private final long workerId;

  private final LongSupplier sequenceSupplier;

  public SnowflakeIdWorker(
      long epoch, long dataCenterId, long workerId, LongSupplier sequenceSupplier) {
    this.epoch = epoch;

    final int timestampBits = defineTimestampBits();
    final int dataCenterIdBits = defineDataCenterBits();
    final int workerIdBits = defineWorkerIdBits();
    final int sequenceBits = defineSequenceBits();
    if ((timestampBits + dataCenterIdBits + workerIdBits + sequenceBits) > 63) {
      throw new IllegalArgumentException("Exceeded max bits");
    }

    final long workerIdMax = ~(-1L << workerIdBits);
    final long dataCenterIdMax = ~(-1L << dataCenterIdBits);
    this.sequenceMax = ~(-1L << sequenceBits);

    if (workerId > workerIdMax || dataCenterId > dataCenterIdMax) {
      throw new IllegalArgumentException(
          "Worker id max : " + workerIdMax + ";Datacenter id max :" + dataCenterIdMax);
    }

    this.workerIdShift = sequenceBits;
    this.dataCenterIdShift = workerIdBits + sequenceBits;
    this.timestampShift = dataCenterIdBits + workerIdBits + sequenceBits;

    this.dataCenterId = dataCenterId;
    this.workerId = workerId;
    this.sequenceSupplier = sequenceSupplier;
  }

  public long nextId() {
    return ((System.currentTimeMillis() - epoch) << timestampShift)
        | (dataCenterId << dataCenterIdShift)
        | (workerId << workerIdShift)
        | (sequenceSupplier.getAsLong() & sequenceMax);
  }

  public static SnowflakeIdWorker version1(
      long epoch, long dataCenterId, long workerId, LongSupplier sequenceSupplier) {
    return new SnowflakeIdWorkerV1(epoch, dataCenterId, workerId, sequenceSupplier);
  }

  protected abstract int defineTimestampBits();

  protected abstract int defineDataCenterBits();

  protected abstract int defineWorkerIdBits();

  protected abstract int defineSequenceBits();
}
